<?php

if (!defined('BASEPATH'))
  exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.3.1
 * @filesource
 */
// ------------------------------------------------------------------------

/**
 * HTML Table Generating Class
 *
 * Lets you create tables manually or from database result objects, or arrays.
 *
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @category	HTML Tables
 * @author		ExpressionEngine Dev Team
 * @link		http://codeigniter.com/user_guide/libraries/uri.html
 */
class CI_Table {

  var $rows = array();
  var $heading = array();
  var $auto_heading = TRUE;
  var $caption = NULL;
  var $template = NULL;
  var $newline = "\n";
  var $empty_cells = "";
  var $function = FALSE;

  public function __construct() {
    log_message('debug', "Table Class Initialized");
  }

  // --------------------------------------------------------------------

  /**
   * Set the template
   *
   * @access	public
   * @param	array
   * @return	void
   */
  function set_template($template) {
    if (!is_array($template)) {
      return FALSE;
    }

    $this->template = $template;
  }

  // --------------------------------------------------------------------

  /**
   * Set the table heading
   *
   * Can be passed as an array or discreet params
   *
   * @access	public
   * @param	mixed
   * @return	void
   */
  function set_heading() {
    $args = func_get_args();
    $this->heading = $this->_prep_args($args);
  }

  // --------------------------------------------------------------------

  /**
   * Set columns.  Takes a one-dimensional array as input and creates
   * a multi-dimensional array with a depth equal to the number of
   * columns.  This allows a single array with many elements to  be
   * displayed in a table that has a fixed column count.
   *
   * @access	public
   * @param	array
   * @param	int
   * @return	void
   */
  function make_columns($array = array(), $col_limit = 0) {
    if (!is_array($array) OR count($array) == 0) {
      return FALSE;
    }

    // Turn off the auto-heading feature since it's doubtful we
    // will want headings from a one-dimensional array
    $this->auto_heading = FALSE;

    if ($col_limit == 0) {
      return $array;
    }

    $new = array();
    while (count($array) > 0) {
      $temp = array_splice($array, 0, $col_limit);

      if (count($temp) < $col_limit) {
        for ($i = count($temp); $i < $col_limit; $i++) {
          $temp[] = '&nbsp;';
        }
      }

      $new[] = $temp;
    }

    return $new;
  }

  // --------------------------------------------------------------------

  /**
   * Set "empty" cells
   *
   * Can be passed as an array or discreet params
   *
   * @access	public
   * @param	mixed
   * @return	void
   */
  function set_empty($value) {
    $this->empty_cells = $value;
  }

  // --------------------------------------------------------------------

  /**
   * Add a table row
   *
   * Can be passed as an array or discreet params
   *
   * @access	public
   * @param	mixed
   * @return	void
   */
  function add_row() {
    $args = func_get_args();
    $this->rows[] = $this->_prep_args($args);
  }

  // --------------------------------------------------------------------

  /**
   * Prep Args
   *
   * Ensures a standard associative array format for all cell data
   *
   * @access	public
   * @param	type
   * @return	type
   */
  function _prep_args($args) {
    // If there is no $args[0], skip this and treat as an associative array
    // This can happen if there is only a single key, for example this is passed to table->generate
    // array(array('foo'=>'bar'))
    if (isset($args[0]) AND (count($args) == 1 && is_array($args[0]))) {
      // args sent as indexed array
      if (!isset($args[0]['data'])) {
        foreach ($args[0] as $key => $val) {
          if (is_array($val) && isset($val['data'])) {
            $args[$key] = $val;
          } else {
            $args[$key] = array('data' => $val);
          }
        }
      }
    } else {
      foreach ($args as $key => $val) {
        if (!is_array($val)) {
          $args[$key] = array('data' => $val);
        }
      }
    }

    return $args;
  }

  // --------------------------------------------------------------------

  /**
   * Add a table caption
   *
   * @access	public
   * @param	string
   * @return	void
   */
  function set_caption($caption) {
    $this->caption = $caption;
  }

  // --------------------------------------------------------------------

  /**
   * Generate the table
   *
   * @access	public
   * @param	mixed
   * @return	string
   */
  function generate($table_data = NULL) {
    // The table data can optionally be passed to this function
    // either as a database result object or an array
    if (!is_null($table_data)) {
      if (is_object($table_data)) {
        $this->_set_from_object($table_data);
      } elseif (is_array($table_data)) {
        $set_heading = (count($this->heading) == 0 AND $this->auto_heading == FALSE) ? FALSE : TRUE;
        $this->_set_from_array($table_data, $set_heading);
      }
    }

    // Is there anything to display?  No?  Smite them!
    if (count($this->heading) == 0 AND count($this->rows) == 0) {
      return 'Undefined table data';
    }

    // Compile and validate the template date
    $this->_compile_template();

    // set a custom cell manipulation function to a locally scoped variable so its callable
    $function = $this->function;

    // Build the table!

    $out = $this->template['table_open'];
    $out .= $this->newline;

    // Add any caption here
    if ($this->caption) {
      $out .= $this->newline;
      $out .= '<caption>' . $this->caption . '</caption>';
      $out .= $this->newline;
    }

    // Is there a table heading to display?
    if (count($this->heading) > 0) {
      $out .= $this->template['thead_open'];
      $out .= $this->newline;
      $out .= $this->template['heading_row_start'];
      $out .= $this->newline;

      foreach ($this->heading as $heading) {
        $temp = $this->template['heading_cell_start'];

        foreach ($heading as $key => $val) {
          if ($key != 'data') {
            $temp = str_replace('<th', "<th $key='$val'", $temp);
          }
        }

        $out .= $temp;
        $out .= isset($heading['data']) ? $heading['data'] : '';
        $out .= $this->template['heading_cell_end'];
      }

      $out .= $this->template['heading_row_end'];
      $out .= $this->newline;
      $out .= $this->template['thead_close'];
      $out .= $this->newline;
    }

    // Build the table rows
    if (count($this->rows) > 0) {
      $out .= $this->template['tbody_open'];
      $out .= $this->newline;

      $i = 1;
      foreach ($this->rows as $row) {
        if (!is_array($row)) {
          break;
        }

        // We use modulus to alternate the row colors
        $name = (fmod($i++, 2)) ? '' : 'alt_';

        $out .= $this->template['row_' . $name . 'start'];
        $out .= $this->newline;

        foreach ($row as $cell) {
          $temp = $this->template['cell_' . $name . 'start'];

          foreach ($cell as $key => $val) {
            if ($key != 'data') {
              $temp = str_replace('<td', "<td $key='$val'", $temp);
            }
          }

          $cell = isset($cell['data']) ? $cell['data'] : '';
          $out .= $temp;

          if ($cell === "" OR $cell === NULL) {
            $out .= $this->empty_cells;
          } else {
            if ($function !== FALSE && is_callable($function)) {
              $out .= call_user_func($function, $cell);
            } else {
              $out .= $cell;
            }
          }

          $out .= $this->template['cell_' . $name . 'end'];
        }

        $out .= $this->template['row_' . $name . 'end'];
        $out .= $this->newline;
      }

      $out .= $this->template['tbody_close'];
      $out .= $this->newline;
    }

    $out .= $this->template['table_close'];

    // Clear table class properties before generating the table
    $this->clear();

    return $out;
  }

  // --------------------------------------------------------------------

  /**
   * Clears the table arrays.  Useful if multiple tables are being generated
   *
   * @access	public
   * @return	void
   */
  function clear() {
    $this->rows = array();
    $this->heading = array();
    $this->auto_heading = TRUE;
  }

  // --------------------------------------------------------------------

  /**
   * Set table data from a database result object
   *
   * @access	public
   * @param	object
   * @return	void
   */
  function _set_from_object($query) {
    if (!is_object($query)) {
      return FALSE;
    }

    // First generate the headings from the table column names
    if (count($this->heading) == 0) {
      if (!method_exists($query, 'list_fields')) {
        return FALSE;
      }

      $this->heading = $this->_prep_args($query->list_fields());
    }

    // Next blast through the result array and build out the rows

    if ($query->num_rows() > 0) {
      foreach ($query->result_array() as $row) {
        $this->rows[] = $this->_prep_args($row);
      }
    }
  }

  // --------------------------------------------------------------------

  /**
   * Set table data from an array
   *
   * @access	public
   * @param	array
   * @return	void
   */
  function _set_from_array($data, $set_heading = TRUE) {
    if (!is_array($data) OR count($data) == 0) {
      return FALSE;
    }

    $i = 0;
    foreach ($data as $row) {
      // If a heading hasn't already been set we'll use the first row of the array as the heading
      if ($i == 0 AND count($data) > 1 AND count($this->heading) == 0 AND $set_heading == TRUE) {
        $this->heading = $this->_prep_args($row);
      } else {
        $this->rows[] = $this->_prep_args($row);
      }

      $i++;
    }
  }

  // --------------------------------------------------------------------

  /**
   * Compile Template
   *
   * @access	private
   * @return	void
   */
  function _compile_template() {
    if ($this->template == NULL) {
      $this->template = $this->_default_template();
      return;
    }

    $this->temp = $this->_default_template();
    foreach (array('table_open', 'thead_open', 'thead_close', 'heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end', 'tbody_open', 'tbody_close', 'row_start', 'row_end', 'cell_start', 'cell_end', 'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end', 'table_close') as $val) {
      if (!isset($this->template[$val])) {
        $this->template[$val] = $this->temp[$val];
      }
    }
  }

  // --------------------------------------------------------------------

  /**
   * Default Template
   *
   * @access	private
   * @return	void
   */
  function _default_template() {
    return array(
        'table_open' => '<table border="0" cellpadding="4" cellspacing="0">',
        'thead_open' => '<thead>',
        'thead_close' => '</thead>',
        'heading_row_start' => '<tr>',
        'heading_row_end' => '</tr>',
        'heading_cell_start' => '<th>',
        'heading_cell_end' => '</th>',
        'tbody_open' => '<tbody>',
        'tbody_close' => '</tbody>',
        'row_start' => '<tr>',
        'row_end' => '</tr>',
        'cell_start' => '<td>',
        'cell_end' => '</td>',
        'row_alt_start' => '<tr>',
        'row_alt_end' => '</tr>',
        'cell_alt_start' => '<td>',
        'cell_alt_end' => '</td>',
        'table_close' => '</table>'
    );
  }

}

/* End of file Table.php */
/* Location: ./system/libraries/Table.php */